/**
 * Copyright 2010 Victor Egea Hernando
 *
 * RockScript is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 * 
 * RockScript is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with RockScript.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <ctime>
#include <cstdlib>
#include <iostream>
#include <paper/paper.h>
#include <corelib/corelib.h>

// VM handler definition
class vm_test1_handler : public paper::vmachine_hnd
{

public:
	// dynamic error report
    void dynError( const paper::vm_error &error )
	{
		std::cout << "<< error >> " << error.error() << std::endl;
	}

	// 
    void open( const corelib::ustring &path )
	{

	}

	// 
    void compile( const corelib::ustring &path )
	{

	}

	// 
    corelib::uint8 getc( void )
	{

	}

	// 
    void close()
	{

	}

};

class test{

public:
	//
	int main();
	// 
	int load_class_method();
	//
	int load_code_miscellanea();

protected:
	// VM & handler
	paper::vmachine vm;
	vm_test1_handler hnd;
	// classes and methods
	paper::vm_class* _class;
	paper::method* _method3;

};

int test::main(){
	// set handler
	vm.set_hnd(hnd);

	// load all libraries
	load_all_libs( vm );

	int ret = 0;
	// load classes and methods
	if(ret = load_class_method() | load_code_miscellanea()){
		return ret;
	}

	// call function
	if(!vm.run(_class->name(), _method3->meta_name())){
		return -1;
	}

	return 0;
}

int test::load_class_method(){
	/** 
	 * add class
	 */
	corelib::pair<paper::vm_class*, bool> testc = vm.add_class( "test.Test" );
	// check addition
	if(!testc.second){ return -1; }
	// get class
	_class = testc.first;

	/** 
	 * add methods
	 */	

	corelib::pair<paper::method*, bool> testm3 = _class->add_method( "gc_test", "mthd_1_", 1 );
	// check addition
	if(!testm3.second){ return -1; }
	// get class
	_method3 = testm3.first;

	//
	return 0;
}

int test::load_code_miscellanea(){
	/* 
	 * corelib.Prompt.println( "Hello " + "World!!" )
	 *
	 * Not assigned new object deleted by the GC with some
	 * help from the compiler unlocking them manually
	 */

	// allocate memory
	if(	!_method3->set_stack_frame_size( 0, 5 ) ){
		std::cout << "error allocating method3 memory" << std::endl;
		return -1;
	}

	// set method static
	_method3->set_flags( paper::FLG_MTHD_STATIC );

	// get classes
	paper::vm_class *string_class = vm.get_class("corelib.core.String").first;
	paper::vm_class *prompt_class = vm.get_class("corelib.Prompt").first;
	// get methods
	corelib::uint32 str_cnstr_idx, str_addition_idx, println_idx;
	string_class->get_method_idx( "cnstr_1_", str_cnstr_idx );
	string_class->get_method_idx( "op_1_addition", str_addition_idx );
	prompt_class->get_method_idx( "mthd_1_println", println_idx );
	// add constants
	paper::__register reg;
	regstring( reg ) = new corelib::ustring( "Hello " );
	corelib::uint32 idx1 = _method3->add_constant( reg );
	regstring( reg ) = new corelib::ustring( "World!!" );
	corelib::uint32 idx2 = _method3->add_constant( reg );

	_method3->add_inst_b(paper::OP_LOADIZ,15,0);
	_method3->add_inst_b(paper::OP_LOADIZ,14,10000);
	_method3->add_inst_a(paper::OP_EQI,14,15,0);
	_method3->add_inst_b(paper::OP_RCJMP,0,22); // LOOP (DOWN)

	// R13 = corelib.core.String("World!!")
	_method3->add_inst_b( paper::OP_NEW,13,string_class->idx() );
	_method3->add_inst_a( paper::OP_LOADARG,0,13,0 );
	_method3->add_inst_b( paper::OP_LOADMK,11, idx2 );
	_method3->add_inst_a( paper::OP_LOADARG,1,11,0 );
	_method3->add_inst_b( paper::OP_KCCALL,str_cnstr_idx,string_class->idx() );
	// R12 = corelib.core.String("Hello ")
	_method3->add_inst_b( paper::OP_NEW,12,string_class->idx() );
	_method3->add_inst_a( paper::OP_LOADARG,0,12,0 );
	_method3->add_inst_b( paper::OP_LOADMK,11, idx1 );
	_method3->add_inst_a( paper::OP_LOADARG,1,11,0 );
	_method3->add_inst_b( paper::OP_KCCALL,str_cnstr_idx,string_class->idx() );
	// R10 = R12 + R13
	_method3->add_inst_a( paper::OP_LOADARG,0,12,0 );
	_method3->add_inst_a( paper::OP_LOADARG,1,13,0 );
	_method3->add_inst_b( paper::OP_KCCALL,str_addition_idx,string_class->idx() );
	// unlock objects
	_method3->add_inst_a( paper::OP_DELETE,12,0,0 );
	_method3->add_inst_a( paper::OP_DELETE,13,0,0 );
	// corelib.Prompt.println( R10 )
	_method3->add_inst_a( paper::OP_MOVE,11,10,0 );
	_method3->add_inst_a( paper::OP_LOADARG,1,11,0 );
	_method3->add_inst_b( paper::OP_KCCALL,println_idx,prompt_class->idx() );
	_method3->add_inst_a( paper::OP_DELETE,11,0,0 );

	_method3->add_inst_b( paper::OP_DECI,0,REG_TOOGLE(14));
	_method3->add_inst_b( paper::OP_RJMP,0,-22 ); // LOOP (UP)
-
	// return
	_method3->add_inst_b(paper::OP_RETURN,10,0);

	return 0;
}

int main(char *argv[], int argc)
{
	test test1;
	return test1.main();
}

